package com.jocata;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.codec.language.DoubleMetaphone;

import com.jocata.hibernate.HibernateUtil;
import com.jocata.hibernate.Main;

public class MatchingTwoStrings {
	
	public static boolean metaMatch(String str1, String str2){
		DoubleMetaphone d = new DoubleMetaphone();		
		return d.isDoubleMetaphoneEqual(str1, str2);
	}
	public float getMatchPercentage(String str1, String str2, boolean isEntity){
		List<String> noiseList = null;
		noiseList = HibernateUtil.getCatalogList();
		List<String> aliasesList = HibernateUtil.getAllAliases();
		HashMap<Integer, ArrayList<String>> aliasGroups = HibernateUtil.getAliasListsByType();
		return getMatchScore(str1, str2, noiseList, aliasesList, aliasGroups, isEntity);
	}
	public float getMatchPercentageFromDB(String str1, String str2, List<String> noiseList, 
			List<String> aliasesList, HashMap<Integer, ArrayList<String>> aliasGroups){
		return getMatchScore(str1, str2, noiseList, aliasesList, aliasGroups, false); 
	}
	private float getMatchScore(String str1, String str2, List<String> noiseList, 
			List<String> aliasesList, HashMap<Integer, ArrayList<String>> aliasGroups, boolean isEntity){
		boolean misMatchedRootName = false;
		RemoveNoiseAndAlias rns=new RemoveNoiseAndAlias();
		String sourceStr=rns.removeNoiseWords(str1.toLowerCase(), noiseList);
		String targetStr=rns.removeNoiseWords(str2.toLowerCase(), noiseList);
		boolean isExactMatch = true;
		DamerauLevenshteinAlgorithm dl = new DamerauLevenshteinAlgorithm(1, 1, 1, 1);
		String sourceStrNew = sourceStr.indexOf(".")!=-1?getDotString(sourceStr):sourceStr;
		String targetStrNew = targetStr.indexOf(".")!=-1?getDotString(targetStr):targetStr;
		//DamerauLevenshtein dl2 = new DamerauLevenshtein();
		//metaMatch("rema", "roma");
		List<String> firstString = new LinkedList<String>(Arrays.asList(sourceStrNew.split("\\s+")));		
		List<String> secondString = new LinkedList<String>(Arrays.asList(targetStrNew.split("\\s+")));
		float lengthFirstString = firstString.size();
		float lengthSecondString = secondString.size();
		ArrayList<Float> scores = new ArrayList<Float>();
		
		/*ArrayList<String> aliases = new ArrayList<String>();
		aliases.add("pvt");
		aliases.add("private");*/
		/*HashMap<Integer, ArrayList<String>> aliasGroups = new HashMap<Integer, ArrayList<String>>();
		aliasGroups.put(1, aliases);*/
		Integer aliasGroupId = null;
		
		int ofacUnMatched = 0;
		for(int i=0; i< firstString.size();i++){
			HashMap<Integer, Integer> arrayElementsDistance = new HashMap<Integer, Integer>();
			isExactMatch = false;	
			float score = 0f;
			if(aliasesList.contains(firstString.get(i))){
				for (Map.Entry<Integer, ArrayList<String>> entry : aliasGroups.entrySet()){
					if(entry.getValue().contains(firstString.get(i))){
						aliasGroupId = entry.getKey();
					}
				}
			}
			for(int j=0; j< secondString.size();j++){				
				if(firstString.get(i).equalsIgnoreCase(secondString.get(j))){
					secondString.remove(j);
					scores.add(100f);
					//score = 100f;
					isExactMatch = true;
					break;
				}else if(aliasGroupId != null && aliasGroups.get(aliasGroupId).contains(secondString.get(j)) ){
					secondString.remove(j);
					scores.add(100f);
					//score = 100f;
					isExactMatch = true;
					break;
				}else{
					if(metaMatch(firstString.get(i), secondString.get(j))){						
						int weight = dl.execute(firstString.get(i), secondString.get(j));							
						arrayElementsDistance.put(j, weight);
					}
					
				}
			}// End of loop Principal Name
			
			boolean overRideScore = false; // parameter to check back and override no. of mis-match characters score	
			boolean isExactMatchFirst = false;
			
			if(!isExactMatch && arrayElementsDistance.size() != 0){
				Map.Entry<Integer, Integer> maxEntry = null;
				for (Map.Entry<Integer, Integer> entry : arrayElementsDistance.entrySet()){
				    if (maxEntry == null || entry.getValue().compareTo(maxEntry.getValue()) > 0)
				    {
				        maxEntry = entry;
				    }
				}
				
				//To check back in OFAC name for case of 'Ram kumar rom' & 'Rom Kumar'
				if(true){
					String stringToCheck = secondString.get(maxEntry.getKey());
					HashMap<Integer, Integer> arrayElementsDistanceInFirst = new HashMap<Integer, Integer>();
					for(int x=i+1;x<firstString.size();x++){
						if(stringToCheck.equalsIgnoreCase(firstString.get(x))){
							firstString.remove(x);
							//scores.add(100f);
							score = 100f;
							isExactMatchFirst = true;
							ofacUnMatched++;
							break;
						}else{
							if(metaMatch(stringToCheck, firstString.get(x))){						
								int weight = dl.execute(stringToCheck, firstString.get(x));							
								arrayElementsDistanceInFirst.put(x, weight);
							}						
						}					
					}
					if(!isExactMatchFirst){
						Map.Entry<Integer, Integer> maxEntryInFirst = null;
						for (Map.Entry<Integer, Integer> entry : arrayElementsDistanceInFirst.entrySet()){
						    if (maxEntryInFirst == null || entry.getValue().compareTo(maxEntryInFirst.getValue()) > 0){
						    	maxEntryInFirst = entry;
						    }
						}
						if(maxEntryInFirst!=null && maxEntry.getValue()<maxEntryInFirst.getValue()){					
							float scoreTemp = 0f;
							if(firstString.get(maxEntryInFirst.getKey()).length()>stringToCheck.length()){
								scoreTemp = (((float)firstString.get(maxEntryInFirst.getKey()).length()-(float)maxEntryInFirst.getValue())/(float)firstString.get(maxEntryInFirst.getKey()).length())*100;
							}else{
								scoreTemp = (((float)stringToCheck.length()-(float)maxEntryInFirst.getValue())/(float)stringToCheck.length())*100;
							}
							score = (scoreTemp+100)/2;//average of Metaphone score and levensthein
							overRideScore = true;
							ofacUnMatched++;
							firstString.remove(maxEntryInFirst.getKey().intValue());
						}
					}
				}
				//End of checking back
				
				if(!isExactMatchFirst && !overRideScore){
					float scoreTemp = 0f;
					if(secondString.get(maxEntry.getKey()).length()>firstString.get(i).length()){
						scoreTemp = (((float)secondString.get(maxEntry.getKey()).length()-(float)maxEntry.getValue())/(float)secondString.get(maxEntry.getKey()).length())*100;
					}else{
						scoreTemp = (((float)firstString.get(i).length()-(float)maxEntry.getValue())/(float)firstString.get(i).length())*100;
					}
					score = (scoreTemp+100)/2;//average of Metaphone score and levensthein
				}
				scores.add(score);
				secondString.remove(maxEntry.getKey().intValue());				
			}else if(!isExactMatch && arrayElementsDistance.size() == 0){
				ofacUnMatched++;
				if(isEntity && i==0){
					misMatchedRootName = true;
				}
			}			
		}// end of loop OFAC Name
		
		//Calculating unmatched no.of words percentage
		float unMatchedWords = (ofacUnMatched>secondString.size())?ofacUnMatched:secondString.size();
		float maxInputStringLength = (lengthFirstString>lengthSecondString)?lengthFirstString:lengthSecondString;
		//float unMatchedPercent = (unMatchedWords/maxInputStringLength)/100;
		
		float totalScore = 0f;
		float avgScore = 0f;
		for(int p=0;p<scores.size();p++){
			//System.out.println(scores.get(p));
			totalScore = totalScore + scores.get(p);			
		}
		avgScore = totalScore/(scores.size()+unMatchedWords);
		if(misMatchedRootName){
			avgScore = avgScore>70?avgScore-20:avgScore;
		}
		System.out.println("Score is: "+avgScore);
		return avgScore;
	}
	public String getDotString(String str){
		String[] str2 = str.split("\\.");
		int maxLength = 0;
		String maxLengthString = null;
		//System.out.println(str.indexOf("."));
		for (String strTemp : str2) {
			if(maxLength < strTemp.length()){
				maxLength = strTemp.length();
				maxLengthString = strTemp;
			}
		}
		if(!maxLengthString.startsWith("\\s")){
			return str.replaceFirst(maxLengthString, " "+maxLengthString).trim();
		}
		return str;
	}
}
